package umls.interfaces.pubmed.server;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Semaphore;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import metamap.proxy.client.MetamapClient;

import org.apache.log4j.Logger;
import org.xml.sax.SAXException;

import umls.interfaces.pubmed.server.jaxb.esearch.Count;
import umls.interfaces.pubmed.server.jaxb.esearch.ESearchResult;
import umls.interfaces.pubmed.server.jaxb.esearch.QueryKey;
import umls.interfaces.pubmed.server.jaxb.esearch.WebEnv;
import umls.interfaces.pubmed.server.sax.PubmedHandler;

class UncachedMapper implements Runnable, AbstractMapper {
	private static Logger logger = Logger
			.getLogger("umls.interfaces.pubmed.server");
	private static String srcurl = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi";
	private static String fetchurl = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi";
	private JAXBContext jc;
	private Unmarshaller us;
	private BlockingQueue<MetamapOutput> queue;

	private static final Semaphore coda = new Semaphore(10, true);
	String[] arr;
	File dir;
	UncachedMapper(){
		if (jc==null || us== null)
			try {
				jc = JAXBContext
						.newInstance("umls.interfaces.pubmed.server.jaxb.esearch");
				us = jc.createUnmarshaller();
			} catch (JAXBException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}
	
	UncachedMapper(BlockingQueue<MetamapOutput> q, String[] a) {
		this();
		queue = q;
		arr = a;
		

	}

	public void run() {

		coda.acquireUninterruptibly();
		MetamapOutput output = null;
		// logger.debug("Metamap, coda "+ coda.getQueueLength());
		try {
			output = metamap(arr);
		} catch (NoMatchException e) {
			String[] arr1 = { arr[0], arr[1] };
			String[] arr2 = { arr[0], arr[2] };
			try {
				MetamapOutput out = metamap(arr1);
				out.add(metamap(arr2));
				output = metamap(arr1).add(metamap(arr2));
				// queue.put(metamap(arr2));

			} catch (NoMatchException e1) {
			}

		} finally {
			if (output == null)
				output = new MetamapOutput();
			try {
				queue.put(output);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			coda.release();
		}

	}

	private MetamapOutput metamap(String[] arr) throws NoMatchException {
		logger.trace("Inizio download e mappatura abstract");
		MetamapOutput abstr = new MetamapOutput(
				new LinkedList<PubMedAbstractMapped>(), arr);
		BufferedReader query = null;
		try {
			query = search(arr);
		} catch (Exception e) {
			logger.debug(e.getMessage());
			throw new NoMatchException(e.getMessage());
		}
		ESearchResult result = null;
		try {
			result = (ESearchResult) us.unmarshal(query);
		} catch (JAXBException e1) {
			logger.debug("Pubmed: ricerca non parsabile");
			throw new NoMatchException("Parsing PubMedFallito");

		}
		String max = null, querykey = null, webenv = null;

		for (java.lang.Object o : result
				.getCountOrRetMaxOrRetStartOrQueryKeyOrWebEnvOrIdListOrTranslationSetOrTranslationStackOrQueryTranslationOrERROR()) {

			if (o.getClass().equals(Count.class)) {
				Count c = (Count) o;
				max = c.getvalue();
			}
			if (o.getClass().equals(QueryKey.class)) {
				querykey = QueryKey.class.cast(o).getvalue();
			}
			if (o.getClass().equals(WebEnv.class)) {
				webenv = WebEnv.class.cast(o).getvalue();
			}
		}
		if (max == null || max.equals("0")) {
			logger.trace("Pubmed: ricerca restituisce 0");
			throw new NoMatchException("ricerca nulla");
		}
		int total = Integer.decode(max);
		if (total > 5)
			total = 5;
		// Map<String, Future<List<String>>> futuremap = new
		// LinkedHashMap<String, Future<List<String>>>();
		InputStream res = null;
		try {
			res = searchEnv(querykey, webenv, 0, total);
		} catch (Exception e) {
			logger.trace("Parsing pubmed imperfetto: " + querykey);
			throw new NoMatchException("ricerca nulla");

		}
		abstr.setKeys(arr);
		Map<String, String> abstracts = getAbracts(res);
		/*
		 * for (String id : abstracts.keySet()) { PubMedAbstractMapped doc = new
		 * PubMedAbstractMapped(id, MetamapClient.testWork(abstracts.get(id)));
		 * abstr.getAbstrs().add(doc); }
		 */
		abstr.getAbstrs().addAll(map(abstracts));
		return abstr;
	}

	public List<PubMedAbstractMapped> map(Map<String, String> abstracts) {
		List<PubMedAbstractMapped> result = new LinkedList<PubMedAbstractMapped>();
		for (String id : abstracts.keySet()) {
			PubMedAbstractMapped doc = new PubMedAbstractMapped(id,
					MetamapClient.blockingQuery(abstracts.get(id)));
			result.add(doc);
		}
		return result;

	}

	private BufferedReader search(String[] names) throws Exception {
		if (names == null) {
			throw new Exception("names nullo");
		}
		BufferedReader rd = null;
		StringBuffer sb = new StringBuffer();
		if (names.length > 0) {
			sb.append(names[0]);
			for (int i = 1; i < names.length; i++) {
				sb.append("+" + names[i]);
			}
		}
		String totalName = sb.toString();
		totalName = totalName.replaceAll(" ", "+");
		try {
			URL url = new URL(srcurl + "?db=pubmed&usehistory=y&term="
					+ totalName);
			URLConnection conn = url.openConnection();

			rd = new BufferedReader(
					new InputStreamReader(conn.getInputStream()));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return rd;
	}

	private Map<String, String> getAbracts(InputStream br) {
		SAXParserFactory factory = SAXParserFactory.newInstance();
		SAXParser saxParser = null;
		PubmedHandler pubhandler = new PubmedHandler();
		try {
			factory.setValidating(false);
			factory
					.setFeature(
							"http://apache.org/xml/features/nonvalidating/load-external-dtd",
							false);
			saxParser = factory.newSAXParser();
			saxParser.parse(br, pubhandler);

		} catch (ParserConfigurationException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (SAXException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return pubhandler.getAbstract();
	}

	private static InputStream searchEnv(String querykey, String webEnv,
			int min, int max) {
		InputStream rd = null;
		try {
			URL url = new URL(fetchurl
					+ "?rettype=abstract?&retmode=xml&query_key=" + querykey
					+ "&retmax=" + max + "&retstart=" + min + "&WebEnv="
					+ webEnv + "&db=Pubmed");
			URLConnection conn = url.openConnection();
			// System.out.println(url.toString());
			rd = conn.getInputStream();
		} catch (IOException e) {
			System.out.println("Errore nella connessione al server pubmed");
		}
		return rd;
	}

	@Override
	public void setCoda(BlockingQueue<MetamapOutput> q) {
		queue=q;
		
	}

	@Override
	public void setInput(String[] _a) {
		arr=_a;
		
	}

}
